<template>
  <div class="kb-create-page">
    <div class="header">
      <h2>知识库向导</h2>
      <p>按步骤完成文档上传与处理</p>
    </div>

    <el-steps :active="step" finish-status="success" align-center>
      <el-step title="上传文档"/>
      <el-step title="参数设置"/>
      <el-step title="分块处理"/>
    </el-steps>

    <!-- 步骤一：上传文档 -->
    <div v-if="step === 0" class="panel">
      <el-form label-width="120px">
        <el-form-item label="目标知识库">
          <el-tag v-if="kbInfo" type="success">{{ kbInfo.name }}</el-tag>
          <el-link v-else type="danger" disabled>未指定</el-link>
        </el-form-item>
        <el-form-item label="选择文件">
          <el-upload
            class="upload"
            drag
            multiple
            :auto-upload="false"
            :on-change="onFileChange"
            :file-list="fileList"
          >
            <i class="el-icon--upload"></i>
            <div class="el-upload__text">
              拖拽文件到此处，或 <em>点击上传</em>
            </div>
            <template #tip>
              <div class="el-upload__tip">仅支持 .txt</div>
            </template>
          </el-upload>
        </el-form-item>
        <div class="inline-chunk-settings" :class="{ disabled: disableChunkSettings }">
          <el-form-item label="分块模式">
            <el-select v-model="settings.chunkMode" placeholder="选择分块模式" :disabled="disableChunkSettings">
              <el-option label="按段落" value="paragraph"/>
              <el-option label="按句子" value="sentence"/>
              <el-option label="按长度" value="length"/>
              <el-option label="自定义分隔符" value="delimiter"/>
            </el-select>
          </el-form-item>
          <template v-if="settings.chunkMode==='length'">
            <el-form-item label="分块长度">
              <el-input-number v-model="settings.chunkSize" :min="1" :step="50" :disabled="disableChunkSettings" />
            </el-form-item>
            <el-form-item label="重叠长度">
              <el-input-number v-model="settings.chunkOverlap" :min="0" :step="10" :disabled="disableChunkSettings" />
            </el-form-item>
          </template>
          <template v-if="settings.chunkMode==='delimiter'">
            <el-form-item label="分隔符">
              <el-input v-model="settings.delimiter" placeholder="示例：\\n 或 \\n\\n" :disabled="disableChunkSettings"/>
            </el-form-item>
          </template>
        </div>
        <el-form-item>
          <el-button type="primary" :disabled="fileList.length===0 || uploading" :loading="uploading" @click="doUpload">
            上传并继续
          </el-button>
          <el-button @click="goBack">返回列表</el-button>
        </el-form-item>
      </el-form>
    </div>

    <!-- 步骤二：参数设置 -->
    <div v-if="step === 1" class="panel">
      <el-form label-width="120px">
        <el-form-item label="Embedding 模型">
          <el-select v-model="settings.embeddingModel" placeholder="选择模型" filterable>
            <el-option v-for="m in embeddingOptions" :key="m.model_id" :label="m.display_name" :value="m.model_name"/>
          </el-select>
        </el-form-item>
        <el-form-item label="分块模式">
          <el-select v-model="settings.chunkMode" placeholder="选择分块模式">
            <el-option label="按段落" value="paragraph"/>
            <el-option label="按句子" value="sentence"/>
            <el-option label="按长度" value="length"/>
            <el-option label="自定义分隔符" value="delimiter"/>
          </el-select>
        </el-form-item>
        <template v-if="settings.chunkMode==='length'">
          <el-form-item label="分块长度">
            <el-input-number v-model="settings.chunkSize" :min="1" :step="50" />
          </el-form-item>
          <el-form-item label="重叠长度">
            <el-input-number v-model="settings.chunkOverlap" :min="0" :step="10" />
          </el-form-item>
        </template>
        <template v-if="settings.chunkMode==='delimiter'">
          <el-form-item label="分隔符">
            <el-input v-model="settings.delimiter" placeholder="示例：\\n 或 \\n\\n"/>
          </el-form-item>
        </template>
        <el-form-item>
          <el-button type="primary" @click="toStep(2)">下一步</el-button>
          <el-button @click="toStep(0)">上一步</el-button>
        </el-form-item>
      </el-form>
    </div>

    <!-- 步骤三：分块处理/结果展示（当前上传即处理，直接展示结果） -->
    <div v-if="step === 2" class="panel">
      <div class="result-head">
        <el-tag type="success">处理完成</el-tag>
        <span class="hint">以下为已上传文档的分块与嵌入结果统计</span>
      </div>
      <div class="run-config">
        <span>嵌入模型：<b>{{ getEmbedDisplayName(settings.embeddingModel) }}</b></span>
        <span>分块：<b>{{ prettyChunkMode }}</b></span>
      </div>
      <div class="view-toggle">
        <el-radio-group v-model="viewMode" size="small">
          <el-radio-button label="chunks">按分块</el-radio-button>
          <el-radio-button label="docs">按文档</el-radio-button>
        </el-radio-group>
      </div>
      <el-table v-if="viewMode==='chunks'" ref="resultTable" :data="chunkFlatList" v-loading="processing || chunksLoading" size="small" style="width: 100%" :max-height="resultTableMaxHeight">
        <el-table-column prop="doc_name" label="文档" min-width="200"/>
        <el-table-column prop="chunk_index" label="ID" width="100"/>
        <el-table-column label="分块内容" min-width="400">
          <template #default="{ row }">
            <div class="chunk-line">{{ row.chunk_text }}</div>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="160">
          <template #default="{ row }">
            <el-button size="small" @click="viewChunks(row.doc_id)">查看该文档全部分块</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-table v-else ref="resultTable" :data="uploadResults" v-loading="processing" size="small" style="width: 100%" @expand-change="onExpand" :max-height="resultTableMaxHeight">
        <el-table-column type="expand">
          <template #default="{ row }">
            <div class="chunk-preview">
              <el-skeleton v-if="loadingPreviewMap[row.doc_id]" :rows="3" animated />
              <template v-else>
                <div v-if="!chunkPreviewMap[row.doc_id] || chunkPreviewMap[row.doc_id].length===0" class="hint">无分块</div>
                <ul v-else class="chunk-list">
                  <li v-for="c in chunkPreviewMap[row.doc_id]" :key="c.chunk_index">
                    <span class="idx">#{{ c.chunk_index }}</span>
                    <span class="txt">{{ c.chunk_text }}</span>
                  </li>
                </ul>
                <el-button size="small" @click="viewChunks(row.doc_id)">查看全部分块</el-button>
              </template>
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="doc_name" label="文档" min-width="180"/>
        <el-table-column prop="vector_count" label="向量数" width="120"/>
      </el-table>
      <div class="actions">
        <el-button type="primary" @click="finish">完成</el-button>
        <el-button @click="toStep(1)">上一步</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import { apiService } from '@/services/api'

export default {
  name: 'KnowledgeCreate',
  data() {
    return {
      step: 0,
      kbId: null,
      kbInfo: null,
      fileList: [],
      uploading: false,
      processing: false,
      uploadResults: [],
      settings: {
        embeddingModel: '',
        chunkMode: 'paragraph',
        chunkSize: 500,
        chunkOverlap: 50,
        delimiter: '\\n\\n'
      },
      embeddingOptions: [],
      chunkPreviewMap: {},
      loadingPreviewMap: {},
      viewMode: 'chunks', // 'chunks' or 'docs'
      resultTableMaxPx: 480
    }
  },
  async mounted() {
    const kbId = this.$route.query.kbId
    if (!kbId) {
      this.$message.error('未指定知识库')
      this.$router.replace({ name: 'knowledge' })
      return
    }
    this.kbId = Number(kbId)
    try {
      this.kbInfo = await apiService.getKnowledgeBase(this.kbId)
      // 加载嵌入模型列表
      const modelRes = await apiService.getEmbeddingModels()
      this.embeddingOptions = modelRes.models || []
      if (!this.settings.embeddingModel && this.embeddingOptions.length) {
        this.settings.embeddingModel = this.embeddingOptions[0].model_name
      }
    } catch (e) {
      // ignore
    }
    this.$nextTick(() => {
      window.addEventListener('resize', this.onResize)
    })
  },
  unmounted() {
    window.removeEventListener('resize', this.onResize)
  },
  watch: {
    step() { this.$nextTick(this.onResize) },
    viewMode() { this.$nextTick(this.onResize) },
    uploadResults() { this.$nextTick(this.onResize) }
  },
  computed: {
    prettyChunkMode() {
      if (this.settings.chunkMode === 'length') {
        return `按长度(size=${this.settings.chunkSize}, overlap=${this.settings.chunkOverlap})`
      }
      if (this.settings.chunkMode === 'delimiter') {
        return `按分隔符('${this.settings.delimiter}')`
      }
      return this.settings.chunkMode === 'sentence' ? '按句子' : '按段落'
    },
    disableChunkSettings() {
      return this.fileList.length === 0
    },
    chunkFlatList() {
      const chunks = []
      ;(this.uploadResults || []).forEach(row => {
        if (this.chunkPreviewMap[row.doc_id]) {
          this.chunkPreviewMap[row.doc_id].forEach(chunk => {
            chunks.push({
              ...row,
              chunk_index: chunk.chunk_index,
              chunk_text: chunk.chunk_text
            })
          })
        }
      })
      return chunks
    },
    chunksLoading() {
      return Object.values(this.loadingPreviewMap).some(Boolean)
    },
    resultTableMaxHeight() {
      return `${this.resultTableMaxPx}px`
    }
  },
  methods: {
    getEmbedDisplayName(modelName) {
      const m = (this.embeddingOptions || []).find(x => x.model_name === modelName)
      return m ? m.display_name : modelName
    },
    onResize() {
      try {
        // recalc available height for result table
        const tableRef = this.$refs.resultTable
        const tableEl = tableRef && tableRef.$el ? tableRef.$el : this.$el.querySelector('.el-table')
        if (tableEl) {
          const rect = tableEl.getBoundingClientRect()
          const viewportH = window.innerHeight || document.documentElement.clientHeight
          const bottomReserve = 120 // actions + margins
          const maxH = Math.max(260, viewportH - rect.top - bottomReserve)
          this.resultTableMaxPx = maxH
          if (tableRef && tableRef.doLayout) tableRef.doLayout()
        }
      } catch (_) { /* ignore */ }
    },
    toStep(n) {
      this.step = n
    },
    onFileChange(file, fileList) {
      // 仅保留最新列表
      this.fileList = fileList.map(f => f.raw).filter(Boolean)
    },
    async doUpload() {
      if (!this.kbId || this.fileList.length === 0) return
      this.uploading = true
      try {
        const res = await apiService.uploadDocuments(this.kbId, this.fileList, {
          embeddingModelName: this.settings.embeddingModel,
          chunkMode: this.settings.chunkMode,
          chunkSize: this.settings.chunkMode === 'length' ? this.settings.chunkSize : undefined,
          chunkOverlap: this.settings.chunkMode === 'length' ? this.settings.chunkOverlap : undefined,
          delimiter: this.settings.chunkMode === 'delimiter' ? this.settings.delimiter : undefined
        })
        // 兼容后端返回的 BatchUploadResponse
        this.uploadResults = (res.results || []).map(r => ({
          doc_id: r.doc_id,
          doc_name: r.doc_name,
          vector_count: r.vector_count
        }))
        this.$message.success('上传并处理完成')
        // 预加载每个文档的分块预览（前10条）
        await this.preloadPreviews()
        this.step = 2
      } catch (e) {
        // ignore
      } finally {
        this.uploading = false
      }
    },
    async preloadPreviews() {
      const tasks = (this.uploadResults || []).map(row => this.loadChunkPreview(row.doc_id))
      await Promise.allSettled(tasks)
    },
    async loadChunkPreview(docId) {
      try {
        this.loadingPreviewMap[docId] = true
        const res = await apiService.getDocumentChunks(docId, 1, 10)
        this.chunkPreviewMap[docId] = res.chunks || []
      } catch (e) {
        this.chunkPreviewMap[docId] = []
      } finally {
        this.loadingPreviewMap[docId] = false
      }
    },
    onExpand(row) {
      if (!this.chunkPreviewMap[row.doc_id] && !this.loadingPreviewMap[row.doc_id]) {
        this.loadChunkPreview(row.doc_id)
      }
    },
    viewChunks(docId) {
      this.$router.push({ name: 'document-chunks', params: { docId } })
    },
    goBack() {
      this.$router.push({ name: 'knowledge' })
    },
    finish() {
      this.$message.success('已完成')
      this.$router.push({ name: 'knowledge' })
    }
  }
}
</script>

<style scoped>
.kb-create-page{
  padding: 24px;
  box-sizing: border-box;
  --kb-accent: #4dabf7;
  --kb-accent-weak: rgba(77, 171, 247, 0.15);
  --kb-surface: linear-gradient(180deg, rgba(77,171,247,0.04), rgba(77,171,247,0.02));
  background: var(--kb-surface);
  width: 100%;
  max-width: none;
  margin: 0;
  font-size: 15px;
  min-height: 0;
}
.header h2{
  font-size: 22px;
  margin-bottom: 8px;
  background-image: linear-gradient(120deg, rgba(77,171,247,0.35), rgba(77,171,247,0));
  background-size: 100% 40%;
  background-repeat: no-repeat;
  background-position: 0 80%;
}
.header p{
  color: var(--muted);
  font-size: 14px;
}
.panel{
  margin-top: 20px;
  background: var(--el-fill-color-blank);
  padding: 20px;
  border-radius: 12px;
  border: 1px solid var(--el-border-color);
  transition: box-shadow .24s ease, border-color .24s ease, transform .24s ease, background-color .24s ease;
  animation: panelIn .32s ease both;
}
.panel + .panel{ margin-top: 16px; }
.panel:hover{
  border-color: var(--kb-accent);
  box-shadow: 0 10px 26px rgba(64, 158, 255, 0.12);
}
.upload{ width: 100%; }
:deep(.el-upload-dragger){ border: 2px dashed var(--el-border-color); background: var(--el-fill-color-light); transition: border-color .24s ease, background-color .24s ease, box-shadow .24s ease, transform .24s ease; padding: 28px 16px; border-radius: 12px; }
:deep(.el-upload-dragger:hover){ border-color: var(--kb-accent); background: rgba(77,171,247,0.06); box-shadow: 0 8px 20px rgba(64, 158, 255, 0.12); }
:deep(.el-upload-dragger.is-dragover){ border-color: var(--kb-accent); background: rgba(77,171,247,0.10); }

/* Steps accent */
:deep(.el-steps){ margin-top: 8px; }
:deep(.el-steps .el-step__head.is-process){ color: var(--kb-accent); border-color: var(--kb-accent); }
:deep(.el-steps .is-success .el-step__head){ color: var(--el-color-success); border-color: var(--el-color-success); }
:deep(.el-steps .el-step__title){ font-size: 14px; }
:deep(.el-steps .el-step__title.is-process){ color: var(--kb-accent); font-weight: 700; }

/* Inline settings */
.inline-chunk-settings{ margin-top: 8px; margin-bottom: 14px; padding: 12px 12px 4px; border-radius: 10px; border: 1px dashed var(--el-border-color); background: var(--el-fill-color-light); transition: background-color .24s ease, border-color .24s ease, box-shadow .24s ease; }
.inline-chunk-settings:hover{ border-color: var(--kb-accent); }
.inline-chunk-settings.disabled{ filter: grayscale(0.6); opacity: 0.6; pointer-events: none; }

/* Form & controls */
:deep(.el-form-item){ margin-bottom: 16px; }
:deep(.el-form-item__label){ font-size: 14px; color: var(--el-text-color-regular); }
:deep(.el-input__wrapper), :deep(.el-select .el-input__wrapper){ transition: border-color .24s ease, box-shadow .24s ease, background-color .24s ease; }
:deep(.el-input__wrapper.is-focus), :deep(.el-select .el-input.is-focus .el-input__wrapper){ box-shadow: 0 0 0 3px rgba(77,171,247,0.12); border-color: var(--kb-accent); }
:deep(.el-input__inner), :deep(.el-textarea__inner){ font-size: 14px; }
:deep(.el-textarea__inner:focus){ border-color: var(--kb-accent); box-shadow: 0 0 0 3px rgba(77,171,247,0.12); }

/* View toggle */
.view-toggle{ display: flex; justify-content: flex-end; margin: 8px 0 10px; }
:deep(.el-radio-button__inner){ padding: 6px 12px; }

/* Tables */
:deep(.el-table__header th){ background: rgba(77,171,247,0.08); color: var(--el-text-color-primary); font-weight: 700; }
:deep(.el-table .cell){ font-size: 14px; }
:deep(.el-table__row--striped .el-table__cell){ background-color: rgba(77,171,247,0.03); }
:deep(.el-table__body tr:hover>td){ background-color: rgba(77,171,247,0.07) !important; }

.hint{ color: var(--el-text-color-secondary); font-size: 13px; }
.actions{ margin-top: 14px; display: flex; gap: 12px; }
.chunk-preview{ padding: 8px 2px; }
.chunk-list{ list-style: none; padding: 0; margin: 0 0 6px; }
.chunk-list .idx{ display: inline-block; width: 36px; color: var(--el-text-color-secondary); }
.chunk-list .txt{ white-space: pre-wrap; line-height: 1.7; }
.chunk-line { white-space: pre-wrap; word-break: break-all; line-height: 1.7; }

/* Buttons */
:deep(.el-button){ transition: transform .18s ease, box-shadow .18s ease, background-color .18s ease, border-color .18s ease; }
:deep(.el-button:active){ transform: translateY(0); }
:deep(.el-button--primary){ box-shadow: 0 2px 0 rgba(64, 158, 255, 0.15) inset; }
:deep(.el-button--primary:hover){ box-shadow: 0 6px 14px rgba(64, 158, 255, 0.18); }

/* Page entrance */
.kb-create-page{ animation: fadeIn .28s ease both; }
@keyframes fadeIn{ from{ opacity: 0; transform: translateY(6px); } to{ opacity: 1; transform: translateY(0); } }
@keyframes panelIn{ from{ opacity: 0; transform: translateY(8px); } to{ opacity: 1; transform: translateY(0); } }

/* AI-style buttons (apply within this page scope) */
.kb-create-page :deep(.el-button){
  background-color: var(--accent, var(--kb-accent));
  border: none;
  color: #ffffff;
  box-shadow: 0 6px 18px rgba(61,85,236,.25);
}
.kb-create-page :deep(.el-button:hover){ filter: brightness(1.05); }
.kb-create-page :deep(.el-button.is-link){
  background-color: var(--accent, var(--kb-accent));
  color: #ffffff;
  padding: 6px 12px;
  border-radius: 6px;
}

/* Keep danger confirmation buttons readable inside popups */
:deep(.el-popconfirm) .el-button--danger{ filter: brightness(0.95); }
</style> 